import streamlit as st
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
import sqlite3
from sqlite3 import Error
import os
import joblib
import io

# 设置中文字体
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题


# 数据库操作函数
def create_connection(db_file):
    """创建数据库连接"""
    conn = None
    try:
        conn = sqlite3.connect(db_file)
        return conn
    except Error as e:
        st.error(f"数据库连接错误: {e}")
    return conn


def create_table(conn, table_name="house_prices"):
    """创建房屋数据表（动态适应不同特征）"""
    try:
        sql = f"""CREATE TABLE IF NOT EXISTS {table_name} (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                );"""
        conn.execute(sql)
    except Error as e:
        st.error(f"创建表错误: {e}")


def add_columns_to_table(conn, table_name, columns):
    """向表中添加新列"""
    try:
        for col_name, col_type in columns.items():
            cursor = conn.execute(f"PRAGMA table_info({table_name})")
            existing_columns = [row[1] for row in cursor.fetchall()]

            if col_name not in existing_columns and col_name not in ['id', 'created_at']:
                conn.execute(f"ALTER TABLE {table_name} ADD COLUMN {col_name} {col_type}")
        conn.commit()
    except Error as e:
        st.error(f"添加列错误: {e}")


def insert_house_data(conn, table_name, house_data, columns):
    """插入房屋数据"""
    try:
        placeholders = ', '.join(['?'] * len(columns))
        sql = f"""INSERT INTO {table_name}({', '.join(columns)})
              VALUES({placeholders})"""
        cur = conn.cursor()
        cur.executemany(sql, house_data)
        conn.commit()
        return cur.lastrowid
    except Error as e:
        st.error(f"插入数据错误: {e}")
        if house_data:
            st.text(f"示例数据: {house_data[0]}")
            st.text(f"列名: {columns}")
        return -1


def select_all_houses(conn, table_name="house_prices"):
    """查询所有房屋数据"""
    cur = conn.cursor()
    cur.execute(f"SELECT * FROM {table_name}")
    rows = cur.fetchall()
    columns = [desc[0] for desc in cur.description]
    return pd.DataFrame(rows, columns=columns)


# 数据预处理函数
def preprocess_data(df, selected_features, target_column):
    """数据预处理：使用选定的特征和目标变量，定义预处理管道"""
    if not isinstance(df, pd.DataFrame):
        st.error("输入数据不是有效的DataFrame")
        return None, None, None, None, None

    if target_column not in df.columns:
        st.error(f"目标列 '{target_column}' 不存在于数据中")
        return None, None, None, None, None

    valid_features = [f for f in selected_features if f in df.columns]
    invalid_features = [f for f in selected_features if f not in df.columns]

    if invalid_features:
        st.warning(f"以下特征不存在于数据中，已自动忽略: {', '.join(invalid_features)}")

    if not valid_features:
        st.error("没有有效的特征可供模型训练")
        return None, None, None, None, None

    # 处理目标列中的缺失值
    try:
        if isinstance(df[target_column], pd.Series):
            if pd.api.types.is_bool_dtype(df[target_column]):
                st.warning(f"目标列 '{target_column}' 是布尔类型，可能不适合作为预测目标")
                has_missing = False
            else:
                has_missing = df[target_column].isnull().any()

            if has_missing:
                missing_count = df[target_column].isnull().sum()
                st.warning(f"目标列 '{target_column}' 中存在 {missing_count} 个缺失值，将被删除")
                df = df.dropna(subset=[target_column])
    except Exception as e:
        st.warning(f"处理目标列缺失值时出错: {e}，将尝试继续处理")

    X = df[valid_features]
    y = df[target_column]

    numeric_features = X.select_dtypes(include=['int64', 'float64']).columns.tolist()
    categorical_features = X.select_dtypes(include=['object', 'category', 'bool']).columns.tolist()
    categorical_features = [f for f in categorical_features if f not in numeric_features]

    preprocessor = ColumnTransformer(
        transformers=[
            ('num', StandardScaler(), numeric_features),
            ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
        ])

    return X, y, preprocessor, numeric_features, categorical_features


# 模型训练函数
def train_models(X_train, y_train, preprocessor):
    models = {
        '线性回归': Pipeline([
            ('preprocessor', preprocessor),
            ('regressor', LinearRegression())
        ]),
        'Ridge回归': Pipeline([
            ('preprocessor', preprocessor),
            ('regressor', Ridge(alpha=1.0))
        ]),
        '随机森林': Pipeline([
            ('preprocessor', preprocessor),
            ('regressor', RandomForestRegressor(n_estimators=100, random_state=42))
        ]),
        '梯度提升': Pipeline([
            ('preprocessor', preprocessor),
            ('regressor', GradientBoostingRegressor(n_estimators=100, random_state=42))
        ])
    }

    trained_models = {}
    for name, model in models.items():
        with st.spinner(f"正在训练{name}..."):
            model.fit(X_train, y_train)
            trained_models[name] = model

    return trained_models


# 模型评估函数
def evaluate_models(models, X_test, y_test):
    results = {}
    for name, model in models.items():
        y_pred = model.predict(X_test)
        mse = mean_squared_error(y_test, y_pred)
        rmse = np.sqrt(mse)
        r2 = r2_score(y_test, y_pred)
        results[name] = {
            'MSE': mse,
            'RMSE': rmse,
            'R²': r2,
            'y_pred': y_pred
        }
    return results


# 可视化函数
def plot_price_distribution(df, target_column):
    if not isinstance(df, pd.DataFrame) or target_column not in df.columns:
        st.error("绘制价格分布时数据无效")
        return

    if not pd.api.types.is_numeric_dtype(df[target_column]):
        st.warning(f"目标列 '{target_column}' 不是数值类型，无法绘制分布")
        return

    plt.figure(figsize=(10, 6))
    sns.histplot(df[target_column], kde=True)
    plt.title(f'{target_column}分布')
    plt.xlabel(target_column)
    plt.ylabel('频数')
    st.pyplot(plt)


def plot_correlation_heatmap(df, selected_features, target_column):
    if not isinstance(df, pd.DataFrame):
        st.error("绘制相关性热力图时数据无效")
        return

    features = selected_features.copy()
    if target_column in df.columns:
        features.append(target_column)
    features = [f for f in features if f in df.columns]

    numeric_df = df[features].select_dtypes(include=['int64', 'float64'])

    plt.figure(figsize=(12, 10))
    corr = numeric_df.corr()
    sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f')
    plt.title('特征相关性热力图')
    st.pyplot(plt)


def plot_feature_importance(model, feature_names):
    if hasattr(model[-1], 'feature_importances_'):
        importances = model[-1].feature_importances_
        indices = np.argsort(importances)[::-1]

        plt.figure(figsize=(12, 8))
        plt.bar(range(min(10, len(indices))), importances[indices[:10]])
        plt.xticks(range(min(10, len(indices))),
                   [feature_names[i] for i in indices[:10]], rotation=45)
        plt.title('特征重要性（前10名）')
        plt.tight_layout()
        st.pyplot(plt)
    else:
        st.info("该模型不支持特征重要性分析")


def plot_model_comparison(results):
    models = list(results.keys())
    r2_scores = [results[model]['R²'] for model in models]

    plt.figure(figsize=(10, 6))
    sns.barplot(x=models, y=r2_scores)
    plt.title('各模型R²得分比较')
    plt.ylabel('R²得分')
    plt.ylim(0, 1)
    plt.xticks(rotation=45)
    st.pyplot(plt)


def plot_prediction_vs_actual(y_test, y_pred, model_name, target_column):
    if not isinstance(y_test, (pd.Series, np.ndarray)) or not isinstance(y_pred, (pd.Series, np.ndarray)):
        st.error("预测值与实际值比较时数据无效")
        return

    plt.figure(figsize=(10, 6))
    plt.scatter(y_test, y_pred, alpha=0.5)
    plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
    plt.xlabel(f'实际{target_column}')
    plt.ylabel(f'预测{target_column}')
    plt.title(f'{model_name}：预测{target_column} vs 实际{target_column}')
    st.pyplot(plt)


# 辅助函数：检查行数据是否包含缺失值
def has_missing_values(row_data, columns):
    """检查行数据中是否包含缺失值，兼容布尔类型"""
    try:
        row_series = pd.Series(row_data, index=columns)
        return row_series.isna().any()
    except Exception as e:
        st.warning(f"检查缺失值时出错: {e}")
        for value in row_data:
            if value is None or pd.isna(value):
                return True
        return False


# 主应用函数
def main():
    st.set_page_config(page_title="智能房屋价格预测系统", layout="wide")
    st.title("🏠 智能房屋价格预测系统")

    db_file = "house_prices.db"
    conn = create_connection(db_file)
    if conn is not None:
        create_table(conn)
    else:
        st.error("无法创建数据库连接")
        return

    menu = ["数据管理", "数据探索", "模型训练", "价格预测"]
    choice = st.sidebar.selectbox("选择功能", menu)

    if not os.path.exists('models'):
        os.makedirs('models')

    # 初始化会话状态变量，增加检查确保目标列有效
    if 'selected_features' not in st.session_state:
        st.session_state.selected_features = []
    if 'target_column' not in st.session_state:
        st.session_state.target_column = None  # 初始化为None而不是默认的'price'

    # 数据管理页面
    if choice == "数据管理":
        st.header("数据管理")

        st.subheader("导入房屋数据")
        st.write("请上传包含房屋特征和价格的CSV文件")
        uploaded_file = st.file_uploader("选择CSV文件", type=["csv"])

        if uploaded_file is not None:
            try:
                df = pd.read_csv(uploaded_file)
                st.success("文件导入成功！")

                st.subheader("数据预览")
                st.dataframe(df.head())

                st.subheader("数据信息")
                buffer = io.StringIO()
                df.info(buf=buffer)
                s = buffer.getvalue()
                st.text(s)

                # 显示所有列名，帮助用户选择正确的目标列
                st.subheader("数据列名")
                st.info(f"数据中包含的列: {', '.join(df.columns.tolist())}")

                # 检查数据类型，特别是布尔类型
                bool_columns = df.select_dtypes(include=['bool']).columns.tolist()
                if bool_columns:
                    st.subheader("布尔类型列警告")
                    st.warning(f"检测到布尔类型列: {', '.join(bool_columns)}")
                    st.info("布尔类型列将被视为分类特征处理")
                    if st.checkbox("显示布尔类型列内容"):
                        st.dataframe(df[bool_columns].head())

                st.subheader("缺失值检查")
                if isinstance(df, pd.DataFrame) and not df.empty:
                    missing = df.isnull().sum()
                    missing = missing[missing > 0]
                    if not missing.empty:
                        st.warning("数据中存在缺失值：")
                        st.dataframe(missing.to_frame(name='缺失值数量'))
                    else:
                        st.success("数据中没有缺失值")
                else:
                    st.error("数据格式无效，无法检查缺失值")

                st.subheader("选择目标列（待预测的房价）")
                if isinstance(df, pd.DataFrame) and not df.empty:
                    # 尝试自动检测可能的价格列
                    price_candidates = [col for col in df.columns if
                                        'price' in col.lower() or 'price' in col or '价格' in col]
                    default_index = 0
                    if price_candidates and price_candidates[0] in df.columns:
                        default_index = df.columns.tolist().index(price_candidates[0])

                    target_column = st.selectbox(
                        "选择表示房价的列",
                        df.columns.tolist(),
                        index=default_index
                    )
                    st.session_state.target_column = target_column
                    st.success(f"已选择目标列: {target_column}")

                    # 检查目标列类型
                    if pd.api.types.is_bool_dtype(df[target_column]):
                        st.warning(f"警告：所选目标列 '{target_column}' 是布尔类型，不适合作为房价预测目标！")
                    elif not pd.api.types.is_numeric_dtype(df[target_column]):
                        st.warning(f"警告：所选目标列 '{target_column}' 不是数值类型，可能影响预测效果")

                    # 检查目标列是否有缺失值
                    try:
                        if isinstance(df[target_column], pd.Series) and not pd.api.types.is_bool_dtype(
                                df[target_column]):
                            if df[target_column].isnull().any():
                                missing_count = df[target_column].isnull().sum()
                                st.warning(
                                    f"目标列 '{target_column}' 中存在 {missing_count} 个缺失值，导入时将自动删除这些记录")
                                if st.checkbox("显示含有缺失价格的记录"):
                                    st.dataframe(df[df[target_column].isnull()])
                    except Exception as e:
                        st.warning(f"检查目标列缺失值时出错: {e}")

                st.subheader("选择特征列（用于预测的特征）")
                if isinstance(df,
                              pd.DataFrame) and not df.empty and 'target_column' in locals() and target_column is not None:
                    all_columns = [col for col in df.columns if col != target_column]
                    default_features = all_columns[:5] if len(all_columns) > 5 else all_columns
                    selected_features = st.multiselect(
                        "选择用于预测的特征（至少选择1个）",
                        all_columns,
                        default=default_features
                    )
                    st.session_state.selected_features = selected_features

                    if len(selected_features) == 0:
                        st.warning("请至少选择一个特征列")
                    else:
                        st.info(f"目标列: {target_column}")
                        st.info(f"选中的特征列: {', '.join(selected_features)}")

                        st.subheader("数据预处理选项")
                        handle_missing = st.radio(
                            "如何处理特征中的缺失值？",
                            ("删除含有缺失值的记录", "用均值/众数填充缺失值")
                        )

                        processed_df = df.copy()

                        # 处理目标列的缺失值
                        try:
                            if isinstance(processed_df[target_column], pd.Series) and not pd.api.types.is_bool_dtype(
                                    processed_df[target_column]):
                                processed_df = processed_df.dropna(subset=[target_column])
                        except Exception as e:
                            st.warning(f"处理目标列缺失值时出错: {e}")

                        # 处理特征列的缺失值
                        if handle_missing == "删除含有缺失值的记录":
                            processed_df = processed_df.dropna(subset=selected_features)
                            st.info(f"删除缺失值后，剩余记录数: {len(processed_df)}")
                        else:
                            try:
                                numeric_features = processed_df[selected_features].select_dtypes(
                                    include=['int64', 'float64']).columns
                                processed_df[numeric_features] = processed_df[numeric_features].fillna(
                                    processed_df[numeric_features].mean())
                            except Exception as e:
                                st.warning(f"填充数值特征缺失值时出错: {e}")

                            try:
                                cat_features = processed_df[selected_features].select_dtypes(
                                    include=['object', 'category', 'bool']).columns
                                for col in cat_features:
                                    if pd.api.types.is_bool_dtype(processed_df[col]):
                                        processed_df[col] = processed_df[col].fillna(processed_df[col].mode()[0])
                                    else:
                                        processed_df[col] = processed_df[col].fillna(processed_df[col].mode()[0])

                                st.info(f"填充缺失值后，记录数保持不变: {len(processed_df)}")
                            except Exception as e:
                                st.warning(f"填充分类特征缺失值时出错: {e}")

                        # 确定每列的数据类型
                        column_types = {}
                        for col in selected_features + [target_column]:
                            if pd.api.types.is_integer_dtype(processed_df[col]):
                                column_types[col] = "INTEGER"
                            elif pd.api.types.is_float_dtype(processed_df[col]):
                                column_types[col] = "REAL"
                            elif pd.api.types.is_bool_dtype(processed_df[col]):
                                column_types[col] = "INTEGER"  # 布尔值在SQL中用0/1存储
                            else:
                                column_types[col] = "TEXT"

                        add_columns_to_table(conn, "house_prices", column_types)

                        if st.button("保存到数据库"):
                            columns_to_save = selected_features + [target_column]
                            house_data = []
                            error_rows = 0

                            for idx, row in processed_df.iterrows():
                                try:
                                    row_data = tuple(row[col] for col in columns_to_save)

                                    if has_missing_values(row_data, columns_to_save):
                                        error_rows += 1
                                        continue

                                    # 转换布尔值为0/1以便数据库存储
                                    cleaned_row = []
                                    for val in row_data:
                                        if isinstance(val, bool):
                                            cleaned_row.append(1 if val else 0)
                                        else:
                                            cleaned_row.append(val)

                                    house_data.append(tuple(cleaned_row))
                                except Exception as e:
                                    error_rows += 1
                                    st.warning(f"处理第 {idx + 1} 行数据时出错: {str(e)[:100]}...")

                            if error_rows > 0:
                                st.info(f"共跳过 {error_rows} 行存在问题的数据")

                            last_id = insert_house_data(conn, "house_prices", house_data, columns_to_save)
                            if last_id != -1:
                                st.success(f"成功导入 {len(house_data)} 条数据到数据库！")

            except Exception as e:
                st.error(f"数据处理错误: {e}")

        st.subheader("数据库中的数据")
        if st.button("加载数据"):
            df = select_all_houses(conn)
            if not df.empty:
                st.dataframe(df.head(10))
                st.write(f"数据库中共有 {len(df)} 条记录")

                # 显示数据库中的列名
                st.subheader("数据库中的列")
                st.info(f"数据库包含的列: {', '.join(df.columns.tolist())}")

                # 确保目标列有效
                if st.session_state.target_column not in df.columns:
                    st.warning(f"当前目标列 '{st.session_state.target_column}' 不在数据库中")
                    # 提供重新选择目标列的选项
                    if len(df.columns) > 0:
                        new_target = st.selectbox(
                            "请重新选择目标列",
                            df.columns.tolist(),
                            index=0
                        )
                        if st.button("更新目标列"):
                            st.session_state.target_column = new_target
                            st.success(f"目标列已更新为: {new_target}")

                st.subheader("数据统计信息")
                # 只显示存在的列
                if st.session_state.target_column in df.columns and st.session_state.selected_features:
                    valid_columns = [col for col in st.session_state.selected_features if col in df.columns]
                    valid_columns.append(st.session_state.target_column)
                    st.dataframe(df[valid_columns].describe())
                else:
                    st.dataframe(df.describe())
            else:
                st.info("数据库中没有数据，请先导入数据")

    # 数据探索页面
    elif choice == "数据探索":
        st.header("数据探索与可视化")

        df = select_all_houses(conn)
        if df.empty:
            st.warning("数据库中没有数据，请先导入数据")
            return

        # 检查并确保目标列有效
        if st.session_state.target_column is None or st.session_state.target_column not in df.columns:
            st.warning("未设置有效的目标列或目标列不存在于数据中")
            # 让用户选择目标列
            target_column = st.selectbox(
                "请选择目标列（待预测的房价）",
                df.columns.tolist(),
                index=0
            )
            st.session_state.target_column = target_column
            st.success(f"已选择目标列: {target_column}")

        # 刷新数据探索页面的目标列
        target_column = st.session_state.target_column

        if not st.session_state.selected_features:
            st.session_state.selected_features = [
                col for col in df.columns
                if col not in ['id', 'created_at', target_column]
            ]
            st.info(f"自动选择了所有可用特征: {', '.join(st.session_state.selected_features)}")

        st.subheader("调整特征选择")
        all_columns = [
            col for col in df.columns
            if col not in ['id', 'created_at', target_column]
        ]
        selected_features = st.multiselect(
            "选择要分析的特征",
            all_columns,
            default=st.session_state.selected_features
        )
        st.session_state.selected_features = selected_features

        if not selected_features:
            st.warning("请至少选择一个特征进行分析")
            return

        st.subheader("数据基本信息")
        # 确保只使用存在的列
        valid_columns = [col for col in selected_features if col in df.columns]
        valid_columns.append(target_column)
        st.dataframe(df[valid_columns].describe())

        st.subheader(f"{target_column}分布")
        plot_price_distribution(df, target_column)

        st.subheader("特征相关性分析")
        plot_correlation_heatmap(df, selected_features, target_column)

        st.subheader(f"特征与{target_column}的关系")
        numeric_features = df[selected_features].select_dtypes(include=['int64', 'float64']).columns.tolist()

        for feature in numeric_features[:5]:
            plt.figure(figsize=(10, 6))
            sns.scatterplot(x=df[feature], y=df[target_column], alpha=0.5)
            plt.title(f'{feature}与{target_column}的关系')
            plt.xlabel(feature)
            plt.ylabel(target_column)
            st.pyplot(plt)

    # 模型训练页面
    elif choice == "模型训练":
        st.header("模型训练与评估")

        df = select_all_houses(conn)
        if df.empty:
            st.warning("数据库中没有数据，请先导入数据")
            return

        # 检查并确保目标列有效
        if st.session_state.target_column is None or st.session_state.target_column not in df.columns:
            st.warning("未设置有效的目标列或目标列不存在于数据中")
            target_column = st.selectbox(
                "请选择目标列（待预测的房价）",
                df.columns.tolist(),
                index=0
            )
            st.session_state.target_column = target_column
            st.success(f"已选择目标列: {target_column}")

        target_column = st.session_state.target_column

        if not st.session_state.selected_features:
            st.session_state.selected_features = [
                col for col in df.columns
                if col not in ['id', 'created_at', target_column]
            ]
            st.info(f"自动选择了所有可用特征: {', '.join(st.session_state.selected_features)}")

        st.subheader("选择训练特征")
        all_columns = [
            col for col in df.columns
            if col not in ['id', 'created_at', target_column]
        ]
        selected_features = st.multiselect(
            "选择用于模型训练的特征",
            all_columns,
            default=st.session_state.selected_features
        )
        st.session_state.selected_features = selected_features

        if not selected_features:
            st.warning("请至少选择一个特征进行模型训练")
            return

        X, y, preprocessor, numeric_features, categorical_features = preprocess_data(
            df, selected_features, target_column
        )

        if X is None:
            return

        test_size = st.slider("测试集比例", 0.1, 0.4, 0.2)
        X_train, X_test, y_train, y_test = train_test_split(
            X, y, test_size=test_size, random_state=42
        )

        st.info(f"数据集划分：训练集 {len(X_train)} 条，测试集 {len(X_test)} 条")

        if st.button("开始训练模型"):
            models = train_models(X_train, y_train, preprocessor)
            results = evaluate_models(models, X_test, y_test)

            st.subheader("模型评估结果")
            results_df = pd.DataFrame({
                '模型': list(results.keys()),
                'MSE': [f"{results[name]['MSE']:.2f}" for name in results],
                'RMSE': [f"{results[name]['RMSE']:.2f}" for name in results],
                'R²': [f"{results[name]['R²']:.4f}" for name in results]
            })
            st.dataframe(results_df)

            st.subheader("模型性能比较")
            plot_model_comparison(results)

            best_model_name = max(results, key=lambda x: results[x]['R²'])
            best_model = models[best_model_name]
            st.success(f"最佳模型是：{best_model_name}，R²得分：{results[best_model_name]['R²']:.4f}")

            joblib.dump(best_model, 'models/best_model.pkl')
            feature_info = {
                'selected_features': selected_features,
                'target_column': target_column
            }
            joblib.dump(feature_info, 'models/feature_info.pkl')
            st.info(f"最佳模型和特征信息已保存")

            st.subheader(f"{best_model_name}：预测值 vs 实际值")
            plot_prediction_vs_actual(
                y_test, results[best_model_name]['y_pred'],
                best_model_name, target_column
            )

            st.subheader("特征重要性分析")
            if hasattr(preprocessor, 'transformers_'):
                num_features = numeric_features
                cat_features = []
                # 检查是否有'cat'转换器
                for name, transformer, _ in preprocessor.transformers_:
                    if name == 'cat':
                        ohe = transformer
                        if hasattr(ohe, 'get_feature_names_out'):
                            cat_features = list(ohe.get_feature_names_out(categorical_features))
                        break

                feature_names = num_features + cat_features
                plot_feature_importance(best_model, feature_names)

    # 价格预测页面
    elif choice == "价格预测":
        st.header("房屋价格预测")

        model_path = 'models/best_model.pkl'
        feature_info_path = 'models/feature_info.pkl'

        if not os.path.exists(model_path) or not os.path.exists(feature_info_path):
            st.warning("尚未训练模型，请先到模型训练页面训练模型")
            return

        model = joblib.load(model_path)
        feature_info = joblib.load(feature_info_path)
        selected_features = feature_info['selected_features']
        target_column = feature_info['target_column']

        df = select_all_houses(conn)
        if df.empty:
            st.warning("数据库中没有数据，无法获取特征参考值")
            return

        # 验证目标列是否存在
        if target_column not in df.columns:
            st.error(f"模型训练时使用的目标列 '{target_column}' 不在当前数据中")
            return

        st.subheader("输入房屋参数")

        input_data = {}
        numeric_features = df[selected_features].select_dtypes(include=['int64', 'float64']).columns.tolist()
        categorical_features = [f for f in selected_features if f not in numeric_features]

        cols = st.columns(2)
        col_idx = 0

        for feature in numeric_features:
            with cols[col_idx % 2]:
                min_val = df[feature].min()
                max_val = df[feature].max()
                mean_val = df[feature].mean()

                if df[feature].dtype == 'int64':
                    input_data[feature] = st.number_input(
                        feature,
                        min_value=int(min_val),
                        max_value=int(max_val),
                        value=int(mean_val)
                    )
                else:
                    input_data[feature] = st.number_input(
                        feature,
                        min_value=float(min_val),
                        max_value=float(max_val),
                        value=float(mean_val),
                        step=float((max_val - min_val) / 100) if max_val > min_val else 0.1
                    )
            col_idx += 1

        for feature in categorical_features:
            with cols[col_idx % 2]:
                unique_vals = df[feature].unique()
                unique_vals_str = [str(val) for val in unique_vals]

                default_val = str(df[feature].mode().iloc[0]) if not df[feature].mode().empty else unique_vals_str[0]

                input_str = st.selectbox(
                    feature,
                    unique_vals_str,
                    index=unique_vals_str.index(default_val) if default_val in unique_vals_str else 0
                )

                try:
                    input_val = float(input_str)
                    if input_val.is_integer():
                        input_val = int(input_val)
                except ValueError:
                    input_val = input_str

                input_data[feature] = input_val

            col_idx += 1

        if st.button(f"预测{target_column}"):
            input_df = pd.DataFrame([input_data])
            input_df = input_df[selected_features]

            with st.spinner(f"正在预测{target_column}..."):
                prediction = model.predict(input_df)

            st.subheader(f"{target_column}预测结果")
            st.success(f"预测{target_column}: ¥{prediction[0]:.2f}")

            if hasattr(model[-1], 'feature_importances_') and st.checkbox("查看特征对预测的影响"):
                preprocessor = model.named_steps['preprocessor']

                num_features = [f for f in numeric_features if f in selected_features]
                cat_features = []

                # 检查是否有'cat'转换器
                for name, transformer, _ in preprocessor.transformers_:
                    if name == 'cat':
                        ohe = transformer
                        cat_features_original = [f for f in categorical_features if f in selected_features]
                        if hasattr(ohe, 'get_feature_names_out'):
                            cat_features = list(ohe.get_feature_names_out(cat_features_original))
                        break

                feature_names = num_features + cat_features
                importances = model[-1].feature_importances_

                importance_df = pd.DataFrame({
                    '特征': feature_names,
                    '重要性': importances
                }).sort_values(by='重要性', ascending=False)

                st.subheader("对预测影响最大的特征")
                st.dataframe(importance_df.head(10))

                plt.figure(figsize=(10, 6))
                sns.barplot(x='重要性', y='特征', data=importance_df.head(10))
                plt.title(f'对{target_column}预测影响最大的特征')
                st.pyplot(plt)

    conn.close()


if __name__ == "__main__":
    main()
